package nachos.network;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.Random;

import nachos.machine.*;
import nachos.threads.*;
import nachos.userprog.*;
import nachos.vm.*;

/**
 * A <tt>VMProcess</tt> that supports networking syscalls.
 */
public class NetProcess extends UserProcess {
    /**
     * Allocate a new process.
     */
    public NetProcess() {
	super();
	address = Machine.networkLink().getLinkAddress();
	System.out.println("Address is " +address);
		for(int i = 0; i < openFileAmount; i++)
		{
			fileTrackerID[i] = 0;
			fileTrackerSentID[i] = 0;
		}
    }

//  public int connect(int host, int port)
	//@SuppressWarnings({ "unused", "unused" }) //I don't know what this does, but eclipse recommended it
	public int connect(int host, int port){
		//source link can be found by Machine.networkLink().getLinkAddress()
		System.out.println("connecting");
		Random r= new Random();
		int srcLink = Machine.networkLink().getLinkAddress();
		if(port < 0 || port > 128)
			return -1;
		
		int srcPort = r.nextInt(1);
		try{
			//sending a SYN packet
			EnhancedPacket p = new EnhancedPacket(host,port, srcLink,srcPort, false,false,false,true,0,new byte[0]);
			NetKernel.getTransport().send(p);
		}catch(MalformedPacketException e){
			System.out.println(e);
		}
		System.out.println("SYN PACKETTT");
		EnhancedPacket synack = NetKernel.getTransport().receive(srcPort, host, port);
		System.out.println("RECEIVED packet");
		if(synack.signal!=3){
			return -1;
		}
		int fileDescriptor = checkForFreeID();
		if(fileDescriptor == -1){
			return -1;
		}
	
		fileTrackerID[fileDescriptor] = 0;
		fileTrackerSentID[fileDescriptor] = 0;
		Socket newSocket = new Socket(srcLink,srcPort,host,port);
		if(socketDescriptor.containsValue(newSocket))
			return -1;
		
		socketDescriptor.put(fileDescriptor, newSocket);
		fileDescriptorStatusTracker[fileDescriptor] = 1;
		return fileDescriptor;
	}

  //  public int accept(int port)
	public int accept(int port){
		System.out.println("accepted");
		EnhancedPacket p;
		while(true)
		{
			p = NetKernel.getTransport().getAndRemoveFirst(port);
			if(p == null)
				break;
			else
				listOfPackets.addLast(p);
		}
		
		if(listOfPackets.size()>0)
			p = listOfPackets.removeFirst();
		else
			return -1;
		if(p.signal != 1) //as long as it's not a SYN packet
			return -1;
		
		Socket newSocket = new Socket(Machine.networkLink().getLinkAddress(),port,p.packet.srcLink,p.srcPort);
		if(socketDescriptor.containsValue(newSocket))
			return -1;
		
		int newFD;
		if((newFD = checkForFreeID()) == -1)
			return -1;
		
		try{
			//EnhancedPacket(dstLink, dstPort, srcLink, srcPort, fin, stp, ack, syn, packetID, contents)
			EnhancedPacket SYNACK = new EnhancedPacket(p.packet.srcLink,p.srcPort, Machine.networkLink().getLinkAddress(),
					port,false,false,true,true,p.packetID,new byte[0]); //SENDING A SYNACK
			NetKernel.getTransport().send(SYNACK);
		}catch(MalformedPacketException e){
			System.out.println(e);
		}
		//sets the values to know that the file descriptor is being taken
		fileTrackerID[newFD]=p.packetID;
		fileTrackerSentID[newFD] = 0;
		socketDescriptor.put(newFD, newSocket);
		fileDescriptorStatusTracker[newFD]=1;
		return newFD;
	}
    
    private static final int
	syscallConnect = 11,
	syscallAccept = 12;
    
   
    /**
     * Handle a syscall exception. Called by <tt>handleException()</tt>. The
     * <i>syscall</i> argument identifies which syscall the user executed:
     *
     * <table>
     * <tr><td>syscall#</td><td>syscall prototype</td></tr>
     * <tr><td>11</td><td><tt>int  connect(int host, int port);</tt></td></tr>
     * <tr><td>12</td><td><tt>int  accept(int port);</tt></td></tr>
     * </table>
     * 
     * @param	syscall	the syscall number.
     * @param	a0	the first syscall argument.
     * @param	a1	the second syscall argument.
     * @param	a2	the third syscall argument.
     * @param	a3	the fourth syscall argument.
     * @return	the value to be returned to the user.
     */
    public int handleSyscall(int syscall, int a0, int a1, int a2, int a3) {
	switch (syscall) {
	case(11):
		connect(a0,a1);
	case(12):
		accept(a0);
	default:
	    return super.handleSyscall(syscall, a0, a1, a2, a3);
	}
    }
    
    //looks if there's a free to use file descriptor
    public int checkForFreeID(){
    	for (int i = 0; i < fileDescriptorStatusTracker.length; i++) {  //loops through and checks the next available spot
			if (fileDescriptorStatusTracker[i] == 0)
				return i;
		}
			return -1;
    }
    
    //------------------VARIABLES
    protected HashMap<Integer,Socket> socketDescriptor = new HashMap<Integer, Socket>();
	static LinkedList<EnhancedPacket> listOfPackets= new LinkedList<EnhancedPacket>() ;
	protected int[] fileTrackerID = new int[openFileAmount]; //keeping track of the sequence#
	protected int[] fileTrackerSentID = new int[openFileAmount]; //need to know what is being sent
	int address;
   // private static int MAX_FILE = 16;
    //OpenFile fileID[] = new OpenFile[MAX_FILE];
    public static Lock lockFromNetProcess= new Lock();
    public TransportOffice testings = new TransportOffice();
    
}
